package dca

import (
	"encoding/binary"
	"encoding/hex"
)

var IV = [8]uint32{0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
	0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e}

// MessagePadding 消息填充
func MessagePadding(message []byte) []byte {
	if message == nil {
		panic("[MessagePadding]空指针异常")
	}
	//fmt.Printf("原始消息: %x\n", message)
	length := uint64(len(message) * 8)
	message = append(message, 0x80)
	if len(message)%64 < 56 {
		zeroByte := make([]byte, 56-len(message)%64)
		message = append(message, zeroByte...)
	} else {
		zeroByte := make([]byte, 56+len(message)%64)
		message = append(message, zeroByte...)
	}
	lengthByte := make([]byte, 8)
	binary.BigEndian.PutUint64(lengthByte, length)
	message = append(message, lengthByte...)
	//fmt.Printf("填充后的消息: \n%x\n", message)
	return message
}

func P0(x uint32) uint32 {
	return x ^ CycleShiftLeft(x, 9) ^ CycleShiftLeft(x, 17)
}

func P1(x uint32) uint32 {
	return x ^ CycleShiftLeft(x, 15) ^ CycleShiftLeft(x, 23)
}

// blockExtend 消息扩展算法,输出两个扩展消息
func blockExtend(block []byte) ([]uint32, []uint32) {
	paddingBlock := make([]uint32, 0, 16)
	for i := 0; i < len(block); i += 4 {
		paddingBlock = append(paddingBlock, binary.BigEndian.Uint32(block[i:i+4]))
	}
	//fmt.Printf("扩展后的消息:\n")
	for i := 16; i < 68; i++ {
		w := P1(paddingBlock[i-16]^paddingBlock[i-9]^CycleShiftLeft(paddingBlock[i-3], 15)) ^
			CycleShiftLeft(paddingBlock[i-13], 7) ^ paddingBlock[i-6]
		paddingBlock = append(paddingBlock, w)
	}
	//fmt.Printf("w0w1---w67:\n%x\n", paddingBlock)
	paddingBlockAnother := make([]uint32, 68)
	copy(paddingBlockAnother, paddingBlock)
	for i := 0; i < 64; i++ {
		paddingBlockAnother[i] = paddingBlockAnother[i] ^ paddingBlockAnother[i+4]
	}
	//fmt.Printf("w0'w1'---w63':\n%x\n", paddingBlockAnother[:64])
	return paddingBlock, paddingBlockAnother
}

func FF(x, y, z uint32, flag int) uint32 {
	if flag >= 0 && flag <= 15 {
		return x ^ y ^ z
	} else {
		return (x & y) | (x & z) | (y & z)
	}
}

func GG(x, y, z uint32, flag int) uint32 {
	if flag >= 0 && flag <= 15 {
		return x ^ y ^ z
	} else {
		return (x & y) | (^x & z)
	}
}

func getT(flag int) uint32 {
	if flag >= 0 && flag <= 15 {
		return 0x79cc4519
	}
	return 0x7a879d8a
}

func SM3(message []byte) string {
	message = MessagePadding(message)
	blockGroup := make([][]byte, 0, len(message)/64)
	for i := 0; i < len(message); i += 64 {
		blockGroup = append(blockGroup, message[i:i+64])
	}
	originIV := IV
	//fmt.Printf("迭代压缩中间值：%x\n", IV)
	for _, block := range blockGroup {
		initIV := originIV
		paddingBlock, paddingBlockAnother := blockExtend(block)
		for i := 0; i < 64; i++ {
			SS1 := CycleShiftLeft(CycleShiftLeft(initIV[0], 12)+initIV[4]+CycleShiftLeft(getT(i), i%32), 7)
			SS2 := SS1 ^ CycleShiftLeft(initIV[0], 12)
			TT1 := FF(initIV[0], initIV[1], initIV[2], i) + initIV[3] + SS2 + paddingBlockAnother[i]
			TT2 := GG(initIV[4], initIV[5], initIV[6], i) + initIV[7] + SS1 + paddingBlock[i]
			initIV[3] = initIV[2]
			initIV[2] = CycleShiftLeft(initIV[1], 9)
			initIV[1] = initIV[0]
			initIV[0] = TT1
			initIV[7] = initIV[6]
			initIV[6] = CycleShiftLeft(initIV[5], 19)
			initIV[5] = initIV[4]
			initIV[4] = P0(TT2)
			//fmt.Printf("i = %d %x\n", i, initIV)
		}
		for j := 0; j < 8; j++ {
			originIV[j] = initIV[j] ^ originIV[j]
		}
		//fmt.Printf("杂凑值：%x\n", originIV)
	}
	sum := make([]byte, 32)
	for i := 0; i < 8; i++ {
		j := i * 4
		sum[j] = byte(originIV[i] >> 24)
		sum[j+1] = byte(originIV[i] >> 16)
		sum[j+2] = byte(originIV[i] >> 8)
		sum[j+3] = byte(originIV[i])
	}
	//fmt.Printf("sum值：\n%v	\n", sum)
	return hex.EncodeToString(sum)
}
